import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
import chart_studio.plotly as py
import cufflinks as cf
%matplotlib inline
# Plotly
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
init_notebook_mode(connected=True)
# Utiliser plotly en local
cf.go_offline()
# créer des grafs et les customiser
import plotly.graph_objects as go
client = pd.read_csv('customers.csv', sep = ',' )
client.head()
| client_id | sex | birth | |
|---|---|---|---|
| 0 | c_4410 | f | 1967 |
| 1 | c_7839 | f | 1975 |
| 2 | c_1699 | f | 1984 |
| 3 | c_5961 | f | 1962 |
| 4 | c_5320 | m | 1943 |
produit = pd.read_csv('products.csv', sep = ',' )
produit.head()
| id_prod | price | categ | |
|---|---|---|---|
| 0 | 0_1421 | 19.99 | 0 |
| 1 | 0_1368 | 5.13 | 0 |
| 2 | 0_731 | 17.99 | 0 |
| 3 | 1_587 | 4.99 | 1 |
| 4 | 0_1507 | 3.99 | 0 |
Ce fichier comprend 4 variables :
vente = pd.read_csv('transactions.csv', sep = ',' )
vente.head()
| id_prod | date | session_id | client_id | |
|---|---|---|---|---|
| 0 | 0_1518 | 2022-05-20 13:21:29.043970 | s_211425 | c_103 |
| 1 | 1_251 | 2022-02-02 07:55:19.149409 | s_158752 | c_8534 |
| 2 | 0_1277 | 2022-06-18 15:44:33.155329 | s_225667 | c_6714 |
| 3 | 2_209 | 2021-06-24 04:19:29.835891 | s_52962 | c_6941 |
| 4 | 0_1509 | 2023-01-11 08:22:08.194479 | s_325227 | c_4232 |
L'id de la session ne nous sera d'aucune utilité ici, nous ne le gardons pas
# vente = vente[["id_prod", "date", "client_id"]]
# vente.head()
# client.info()
# vente.info()
# produit.info()
# client.describe()
produit.describe()
# vente.describe()
| price | categ | |
|---|---|---|
| count | 3287.000000 | 3287.000000 |
| mean | 21.856641 | 0.370246 |
| std | 29.847908 | 0.615387 |
| min | -1.000000 | 0.000000 |
| 25% | 6.990000 | 0.000000 |
| 50% | 13.060000 | 0.000000 |
| 75% | 22.990000 | 1.000000 |
| max | 300.000000 | 2.000000 |
Nous constatons d'ores et déjà un problème au niveau des données :
En effet, la ligne MIN indique un prix négatif.
# client.info()
# vente.info()
# produit.info()
# client.describe()
# produit.describe()
vente.describe()
| id_prod | date | session_id | client_id | |
|---|---|---|---|---|
| count | 679532 | 679532 | 679532 | 679532 |
| unique | 3267 | 679371 | 342316 | 8602 |
| top | 1_369 | test_2021-03-01 02:30:02.237413 | s_0 | c_1609 |
| freq | 2252 | 13 | 200 | 25488 |
Ici aussi, nous constatons un problème, cette fois-ci avec le champs DATE.
La ligne TOP indique une date commençant par "test_"
# vérification des NaN fichier CLIENTS
client.isnull().sum()
client_id 0 sex 0 birth 0 dtype: int64
# vérification des NaN fichier PRODUITS
produit.isnull().sum()
id_prod 0 price 0 categ 0 dtype: int64
# vérification des NaN fichier VENTES
vente.isnull().sum()
id_prod 0 date 0 session_id 0 client_id 0 dtype: int64
# recherche des doublons dans le fichier CLIENTS
client.duplicated().sum()
0
# recherche des doublons dans le fichier PRODUITS
produit.duplicated().sum()
0
# recherche des doublons dans le fichier VENTE
vente.duplicated().sum()
126
# vérification des formats du fichier CLIENTS
client.dtypes
client_id object sex object birth int64 dtype: object
La variable "client_id" est de type objet car le code se compose ainsi : c_xxxx
# vérification des formats du fichier PRODUITS
produit.dtypes
id_prod object price float64 categ int64 dtype: object
la variable "product_id" est de type object car le code se compose ainsi : x_xxx
# vérification des formats du fichier VENTES
vente.dtypes
id_prod object date object session_id object client_id object dtype: object
la variable "date" est de type objet et non date
# suppression des doublons dans le fichier VENTE
vente = vente.drop_duplicates()
# et je vérifie
vente.duplicated().sum()
0
# Recherche des valeurs test dans le fichier VENTE :
list_v = []
for column in vente:
list_v.append(vente.loc[vente[column].str.contains("test")])
list_v
[Empty DataFrame
Columns: [id_prod, date, session_id, client_id]
Index: [],
id_prod date session_id client_id
3019 T_0 test_2021-03-01 02:30:02.237419 s_0 ct_0
5138 T_0 test_2021-03-01 02:30:02.237425 s_0 ct_0
9668 T_0 test_2021-03-01 02:30:02.237437 s_0 ct_1
10728 T_0 test_2021-03-01 02:30:02.237436 s_0 ct_0
15292 T_0 test_2021-03-01 02:30:02.237430 s_0 ct_0
... ... ... ... ...
577324 T_0 test_2021-03-01 02:30:02.237424 s_0 ct_0
593065 T_0 test_2021-03-01 02:30:02.237422 s_0 ct_1
607892 T_0 test_2021-03-01 02:30:02.237412 s_0 ct_0
626049 T_0 test_2021-03-01 02:30:02.237422 s_0 ct_0
670680 T_0 test_2021-03-01 02:30:02.237449 s_0 ct_1
[74 rows x 4 columns],
Empty DataFrame
Columns: [id_prod, date, session_id, client_id]
Index: [],
Empty DataFrame
Columns: [id_prod, date, session_id, client_id]
Index: []]
Nous avons donc 74 lignes de test.
NB : les identifiants produits de type T_0 sont des tests, de même que les identifiants client de type CT_0 ou CT_1
# Suppression des valeurs test dans le fichier VENTE :
# récupère les index des lignes contenant 'test'
test_v = list_v[1].index
test_v
# supprime ces lignes du fichier VENTE :
vente = vente.drop(test_v)
vente.shape # mes 74 lignes ont bien été supprimées
(679332, 4)
# Recherche des valeurs test dans le fichier PRODUITS :
# seule la colonne ID_PROD est au format 'texte' :
list_p = produit.loc[produit['id_prod'].str.contains("T")]
list_p
| id_prod | price | categ | |
|---|---|---|---|
| 731 | T_0 | -1.0 | 0 |
Je n'ai qu'une seule ligne contenant un identifiant produit de test.
Je dois ensuite vérifier qu'il n'y a pas d'autre prix négatif.
# recherche des tarifs négatifs dans le fichier PRODUITS :
prix_neg = produit.loc[produit['price']<0]
prix_neg
| id_prod | price | categ | |
|---|---|---|---|
| 731 | T_0 | -1.0 | 0 |
il s'agit de la même ligne.
Je n'ai donc qu'une seule ligne à supprimer dans ce fichier
# Suppression des valeurs test
produit = produit.drop(731)
produit.shape # ma ligne a bien été supprimée
(3286, 3)
# Recherche des valeurs test dans le fichier CLIENTS :
# je ne vais faire la recherche que sur l'identifiant client :
liste_c = client.loc[client['client_id'].str.contains("ct")]
liste_c
| client_id | sex | birth | |
|---|---|---|---|
| 2735 | ct_0 | f | 2001 |
| 8494 | ct_1 | m | 2001 |
# Suppression des valeurs test
client = client.drop(liste_c.index)
client.shape
(8621, 3)
# modification du format de la colonne DATE du fichier VENTE :
vente.head(1)
| id_prod | date | session_id | client_id | |
|---|---|---|---|---|
| 0 | 0_1518 | 2022-05-20 13:21:29.043970 | s_211425 | c_103 |
ce format est de type objet.
de plus, il est trop détaillé : nous n'avons besoin que d'un format de type '%Y-%m-%d %H:%M:%S'
# suppression des informations inutiles :
# tout ce qui est après le point :
vente['date'] = vente['date'].str.split(' ', expand= True)[0]
# transformation du type objet en date
vente['date']=pd.to_datetime(vente['date'], format = '%Y-%m-%d')
vente.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 679332 entries, 0 to 679531 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id_prod 679332 non-null object 1 date 679332 non-null datetime64[ns] 2 session_id 679332 non-null object 3 client_id 679332 non-null object dtypes: datetime64[ns](1), object(3) memory usage: 25.9+ MB
vente.head(1)
| id_prod | date | session_id | client_id | |
|---|---|---|---|---|
| 0 | 0_1518 | 2022-05-20 | s_211425 | c_103 |
Pas de recherche pour les ventes et les clients, car une simple description du fichier nous permet de voir que toutes les dates sont bien entre le 01/03/21 et le 28/02/23 pour ce qui est des ventes, et toutes les dates de naissance sont comprises entre 1929 et 2004.
px.box(produit, x='price')
data = produit['price']
threshold = 3 # choix d'un z-score de 3 càd valeurs avec écart type >= 3
mean = np.mean(data)
std = np.std(data)
z_score_outlier = [i for i in data if (i-mean)/std > threshold]
# afficher la liste des outliers
val_aty = produit.loc[produit['price'].isin(z_score_outlier),:]
val_aty.describe()
| price | categ | |
|---|---|---|
| count | 97.000000 | 97.0 |
| mean | 156.226082 | 2.0 |
| std | 38.937549 | 0.0 |
| min | 111.720000 | 2.0 |
| 25% | 127.990000 | 2.0 |
| 50% | 145.450000 | 2.0 |
| 75% | 175.670000 | 2.0 |
| max | 300.000000 | 2.0 |
Nous constatons que la grande majorité des prix se situe entre 0 et 46.99€.
Au-delà, 97 outliers dont les tarifs vont de 111.72 à 300€.
Ces références sont-elles des valeurs atypiques ?
Cela semble peu probable : il est normal d'avoir des références à un prix standard et une gamme plus onéreuse.
A valider avec le service commercial.
# jointure left car je veux garder toutes les infos du fichier VENTE
# permet d'écarter d'éventuels clients et produits inactifs
d =pd.merge(pd.merge(vente, client, on='client_id', how='left'), produit, on='id_prod')
d.head()
| id_prod | date | session_id | client_id | sex | birth | price | categ | |
|---|---|---|---|---|---|---|---|---|
| 0 | 0_1518 | 2022-05-20 | s_211425 | c_103 | f | 1986 | 4.18 | 0 |
| 1 | 0_1518 | 2021-09-26 | s_95811 | c_6197 | m | 1985 | 4.18 | 0 |
| 2 | 0_1518 | 2021-05-06 | s_30782 | c_682 | f | 1974 | 4.18 | 0 |
| 3 | 0_1518 | 2022-03-16 | s_180057 | c_5932 | f | 1962 | 4.18 | 0 |
| 4 | 0_1518 | 2022-11-12 | s_296584 | c_7217 | f | 1976 | 4.18 | 0 |
Dans un premier temps, nous allons définir nos KPI :
Puis nous allons calculer le CA total :
# le CA total correspond à la somme de la colonne price
CA_total = d["price"].sum()
print("Le chiffre d'affaires total de ces deux dernières années est de : "+ "{:,.2f}".format(round(CA_total, 2)) + '€')
Le chiffre d'affaires total de ces deux dernières années est de : 11,853,728.68€
Calcul du CA mois par mois :
# group by mois, price.sum
d['Année - Mois'] = d['date'].apply(lambda x:x.strftime('%Y-%m'))
ca_mois = d.groupby('Année - Mois')['price'].sum()
ca_mois
Année - Mois 2021-03 482440.61 2021-04 476109.30 2021-05 492943.47 2021-06 484088.56 2021-07 482835.40 2021-08 482284.79 2021-09 507240.68 2021-10 320798.74 2021-11 516167.73 2021-12 525917.28 2022-01 525338.99 2022-02 535571.50 2022-03 515456.53 2022-04 492998.94 2022-05 517132.60 2022-06 496016.12 2022-07 510783.12 2022-08 506467.27 2022-09 494114.53 2022-10 507917.77 2022-11 496664.94 2022-12 510219.50 2023-01 517540.55 2023-02 456679.76 Name: price, dtype: float64
# représentation graphique
fig = go.Figure()
fig.add_trace(go.Scatter(x=ca_mois.index, y=ca_mois.values, mode='lines', name='ca mensuel' ))
Nous constatons d'ores et déjà une chute brutale du CA au mois d'octobre.
Il conviendra d'analyser plus précisément ce point afin :
# identification des données d'octobre
d_oct_21 = d.loc[d['Année - Mois'] == "2021-10"]
d_oct_21
| id_prod | date | session_id | client_id | sex | birth | price | categ | Année - Mois | |
|---|---|---|---|---|---|---|---|---|---|
| 35 | 0_1518 | 2021-10-11 | s_103224 | c_5312 | m | 1977 | 4.18 | 0 | 2021-10 |
| 54 | 0_1518 | 2021-10-26 | s_110388 | c_7048 | m | 1981 | 4.18 | 0 | 2021-10 |
| 60 | 0_1518 | 2021-10-11 | s_103072 | c_2455 | f | 1952 | 4.18 | 0 | 2021-10 |
| 68 | 0_1518 | 2021-10-15 | s_105315 | c_5697 | m | 1988 | 4.18 | 0 | 2021-10 |
| 83 | 0_1518 | 2021-10-12 | s_103594 | c_846 | f | 1981 | 4.18 | 0 | 2021-10 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 679057 | 0_898 | 2021-10-16 | s_105378 | c_4805 | f | 1975 | 1.27 | 0 | 2021-10 |
| 679070 | 0_1806 | 2021-10-31 | s_112884 | c_229 | f | 1987 | 1.61 | 0 | 2021-10 |
| 679079 | 0_1595 | 2021-10-03 | s_99495 | c_4394 | f | 1978 | 2.99 | 0 | 2021-10 |
| 679083 | 0_698 | 2021-10-14 | s_104438 | c_5429 | f | 1968 | 1.44 | 0 | 2021-10 |
| 679108 | 0_1379 | 2021-10-16 | s_105547 | c_5425 | f | 1981 | 2.99 | 0 | 2021-10 |
21599 rows × 9 columns
# comparaison avec le mois d'octobre 2022 et les mois de septembre et de novembre 2021
# création du tableau et insertion des données pour oct 2021
comp = pd.DataFrame(
columns=['oct_21', 'oct_22', 'sept_21', 'nov_21'],
index=['prix_moy', 'ca_mens', 'nb_cat0' ,'nb_cat1', 'nb_cat2', 'nb_total'])
comp['oct_21'] = [round(d_oct_21['price'].mean(),2),d_oct_21['price'].sum(),
d_oct_21.loc[d_oct_21['categ']== 0, 'categ'].count(),d_oct_21.loc[d_oct_21['categ']== 1,
'categ'].count(),d_oct_21.loc[d_oct_21['categ']== 2, 'categ'].count() ,d_oct_21['price'].count()]
# calcul et insertion des données pour oct_2022
d_oct_22 = d.loc[d['Année - Mois'] == "2022-10"]
comp['oct_22'] = [round(d_oct_22['price'].mean(),2),d_oct_22['price'].sum(),
d_oct_22.loc[d_oct_22['categ']== 0, 'categ'].count(),d_oct_22.loc[d_oct_22['categ']== 1,
'categ'].count(),d_oct_22.loc[d_oct_22['categ']== 2, 'categ'].count() ,d_oct_22['price'].count()]
# calcul et insertion des données pour sept_2021
d_sept_21 = d.loc[d['Année - Mois'] == "2021-09"]
comp['sept_21'] = [round(d_sept_21['price'].mean(),2), d_sept_21['price'].sum(),
d_sept_21.loc[d_sept_21['categ']== 0, 'categ'].count(),d_sept_21.loc[d_sept_21['categ']== 1,
'categ'].count(),d_sept_21.loc[d_sept_21['categ']== 2, 'categ'].count() ,d_sept_21['price'].count()]
# calcul et insertion des données pour nov_2021
d_nov_21 = d.loc[d['Année - Mois'] == "2021-11"]
comp['nov_21'] = [round(d_nov_21['price'].mean(),2), d_nov_21['price'].sum(),
d_nov_21.loc[d_nov_21['categ']== 0, 'categ'].count(),d_nov_21.loc[d_nov_21['categ']== 1,
'categ'].count(),d_nov_21.loc[d_nov_21['categ']== 2, 'categ'].count() ,d_nov_21['price'].count()]
comp
# comparer avec octobre 2022
# comparer avec mois avant
# comparer avec mois après
| oct_21 | oct_22 | sept_21 | nov_21 | |
|---|---|---|---|---|
| prix_moy | 14.85 | 17.54 | 15.23 | 18.23 |
| ca_mens | 320798.74 | 507917.77 | 507240.68 | 516167.73 |
| nb_cat0 | 18762.00 | 17664.00 | 23142.00 | 14600.00 |
| nb_cat1 | 1666.00 | 9726.00 | 9268.00 | 12316.00 |
| nb_cat2 | 1171.00 | 1574.00 | 904.00 | 1395.00 |
| nb_total | 21599.00 | 28964.00 | 33314.00 | 28311.00 |
Nous constatons qu'il y a un écart important de chiffre d'affaires entre le mois d'oct 2021 et les autres
(environ 40%)
L'écart concerne les produits de la catégorie 1.
en effet, seulement 1666 produits de cette catégorie ont été vendus en oct 2021 contre une moyenne de 10436 les autres mois.
Il peut donc s'agir d'un problème de disponibilité de ces articles
(problème fournisseur ou autre)
Deux questions se posent alors :
Nous constatons sur le graph un pic au premier trimestre 2022.
Nous allons donc ajouter ces données au comparateur.
# calculs :
# prix moyen
# nb par categorie
# nb de ventes par jour (moyenne)
# données de decembre 21
d_dec_21 = d.loc[d['Année - Mois'] == "2021-12"]
comp['dec_21'] = [round(d_dec_21['price'].mean(),2), d_dec_21['price'].sum(),
d_dec_21.loc[d_dec_21['categ']== 0, 'categ'].count(),d_dec_21.loc[d_dec_21['categ']== 1,
'categ'].count(),d_dec_21.loc[d_dec_21['categ']== 2, 'categ'].count() ,d_dec_21['price'].count()]
# données de janvier 22
d_jan_22 = d.loc[d['Année - Mois'] == "2022-01"]
comp['jan_22'] = [round(d_jan_22['price'].mean(),2), d_jan_22['price'].sum(),
d_jan_22.loc[d_jan_22['categ']== 0, 'categ'].count(),d_jan_22.loc[d_jan_22['categ']== 1,
'categ'].count(),d_jan_22.loc[d_jan_22['categ']== 2, 'categ'].count() ,d_jan_22['price'].count()]
# données de février 22
d_fev_22 = d.loc[d['Année - Mois'] == "2022-02"]
comp['fev_22'] = [round(d_fev_22['price'].mean(),2), d_fev_22['price'].sum(),
d_fev_22.loc[d_fev_22['categ']== 0, 'categ'].count(),d_fev_22.loc[d_fev_22['categ']== 1,
'categ'].count(),d_fev_22.loc[d_fev_22['categ']== 2, 'categ'].count() ,d_fev_22['price'].count()]
# j'organise mon df
comp = comp[['oct_22', 'sept_21', 'oct_21', 'nov_21', 'dec_21', 'jan_22', 'fev_22']]
comp
| oct_22 | sept_21 | oct_21 | nov_21 | dec_21 | jan_22 | fev_22 | |
|---|---|---|---|---|---|---|---|
| prix_moy | 17.54 | 15.23 | 14.85 | 18.23 | 16.20 | 17.90 | 18.1 |
| ca_mens | 507917.77 | 507240.68 | 320798.74 | 516167.73 | 525917.28 | 525338.99 | 535571.5 |
| nb_cat0 | 17664.00 | 23142.00 | 18762.00 | 14600.00 | 19302.00 | 15397.00 | 17281.0 |
| nb_cat1 | 9726.00 | 9268.00 | 1666.00 | 12316.00 | 12259.00 | 12560.00 | 10459.0 |
| nb_cat2 | 1574.00 | 904.00 | 1171.00 | 1395.00 | 896.00 | 1386.00 | 1854.0 |
| nb_total | 28964.00 | 33314.00 | 21599.00 | 28311.00 | 32457.00 | 29343.00 | 29594.0 |
On constate que les mois suivants (novembre, décembre et janvier) montrent
une augmentation du nb d'articles de catégorie 1 vendus.
Ce nombre revient à la normale dès le mois de mars 2022.
De plus, si on fait la moyenne des mois d'oct, nov, decembre et janvier, on trouve 9700, ce qui correspond aux données
des mois de septembre 2021 et octobre 2022.
Nous partons donc de l'hypothèse que les données sont lissées sur les autres mois.
Nous décidons donc de ne pas intervenir sur les données d'octobre 2021.
Ces hypothèses devront être vérifiées auprès des services achat et commercial.
moy_mob = ca_mois.rolling(3).mean() moy_mob
# représentation graphique de la moyenne mobile
# je change le format de l'index en date pour pouvoir afficher le graph
ca_mois.index=pd.to_datetime(ca_mois.index, format = '%Y-%m')
px.scatter(ca_mois, x=ca_mois.index, y=ca_mois.values , opacity=1, color=ca_mois.values, trendline='rolling', trendline_options=dict(window=3),title="Moyenne mobile sur 3 mois",color_continuous_scale='twilight')
on constate une relative stabilité du CA autour de 500k
avec deux pics :
Nous avons vu précédemment une explication possible à ces pics.
# description : prix moy
d[['price']].describe()
| price | |
|---|---|
| count | 679111.000000 |
| mean | 17.454773 |
| std | 18.328998 |
| min | 0.620000 |
| 25% | 8.870000 |
| 50% | 13.990000 |
| 75% | 18.990000 |
| max | 300.000000 |
Les prix s'étendent entre 0.62 et 300€ pour un prix moyen de 17.45€.
# description : ca moy
ca_moy_art = CA_total/679111
ca_moy_mois = CA_total/24
Le CA moyen par article est de 17.45€
Le CA moyen par mois est de 474 149€
# description : nb ventes moy
nb_art = 679332/3266
nb_mois = 679332/24
Le nb de ventes moyen par article est de 208.
Le nb de ventes moyen par mois est de 27 173.
# répartition des prix par catégories :
# plt.figure(figsize=(8,3))
# sns.boxplot(data=d, y='categ', x='price', orient='h', showfliers=False, showmeans=True, palette=["#64ffda", "#536dfe", "#ff6e40"], meanprops={"marker":"s","markerfacecolor":"white"})
# plt.title('Distribution des prix par catégorie')
# plt.show()
px.box(d, x='price', y='categ', orientation='h', points= False, color='categ' , title='Distribution des prix par catégorie')
Catégorie 0 :
Catégorie 1 :
Catégorie 2 :
# top 10 des produits les plus vendus (nb_ventes)
sel_nb_v = d.groupby('id_prod')['id_prod'].count().sort_values(ascending=False).iloc[:10]
nb_v = pd.DataFrame({'id_prod' : sel_nb_v.index,
'nb_ventes' : sel_nb_v.values})
# quel est leur prix
nb_prix = d.loc[d['id_prod'].isin(sel_nb_v.index), ['price', 'id_prod']]
nb_prix = nb_prix.drop_duplicates()
top_nb_ventes = pd.merge(nb_v, nb_prix, on='id_prod', how='left')
# quel est leur CA
top_nb_ventes['CA réalisé'] = top_nb_ventes['nb_ventes'] * top_nb_ventes['price']
print('Top 10 des produits les plus vendus :')
top_nb_ventes
Top 10 des produits les plus vendus :
| id_prod | nb_ventes | price | CA réalisé | |
|---|---|---|---|---|
| 0 | 1_369 | 2252 | 23.99 | 54025.48 |
| 1 | 1_417 | 2189 | 20.99 | 45947.11 |
| 2 | 1_414 | 2180 | 23.83 | 51949.40 |
| 3 | 1_498 | 2128 | 23.37 | 49731.36 |
| 4 | 1_425 | 2096 | 16.99 | 35611.04 |
| 5 | 1_403 | 1960 | 17.99 | 35260.40 |
| 6 | 1_412 | 1951 | 16.65 | 32484.15 |
| 7 | 1_413 | 1945 | 17.99 | 34990.55 |
| 8 | 1_406 | 1939 | 24.81 | 48106.59 |
| 9 | 1_407 | 1935 | 15.99 | 30940.65 |
Les articles les plus vendus appartiennent à la catégorie 1.
Leur prix se situe entre 15.99€ et 24.81€.
# top 10 des produits les plus chers
top_prix = d[['price','id_prod']].drop_duplicates().sort_values(by = 'price', ascending=False).iloc[:10]
# combien de ventes
sel_nb_v2 = d.loc[d['id_prod'].isin(top_prix['id_prod']), 'id_prod']
sel_nb_v2 = pd.DataFrame({'id_prod' : sel_nb_v2.values})
sel_nb_v2= sel_nb_v2.groupby('id_prod')['id_prod'].count()
nb_v2 = pd.DataFrame({'id_prod' : sel_nb_v2.index,
'nb_ventes' : sel_nb_v2.values})
top_prix = pd.merge(top_prix, nb_v2, on='id_prod', how='left')
# quel est leur CA
top_prix['CA réalisé'] = top_prix['nb_ventes'] * top_prix['price']
top_prix = top_prix[['id_prod', 'nb_ventes', 'price', 'CA réalisé']]
print('Top 10 des produits les plus chers :')
top_prix
Top 10 des produits les plus chers :
| id_prod | nb_ventes | price | CA réalisé | |
|---|---|---|---|---|
| 0 | 2_2 | 15 | 300.00 | 4500.00 |
| 1 | 2_76 | 9 | 254.44 | 2289.96 |
| 2 | 2_158 | 142 | 247.22 | 35105.24 |
| 3 | 2_167 | 189 | 236.99 | 44791.11 |
| 4 | 2_30 | 7 | 233.54 | 1634.78 |
| 5 | 2_125 | 7 | 231.99 | 1623.93 |
| 6 | 2_166 | 228 | 230.04 | 52449.12 |
| 7 | 2_40 | 26 | 228.11 | 5930.86 |
| 8 | 2_228 | 75 | 225.17 | 16887.75 |
| 9 | 2_4 | 6 | 222.97 | 1337.82 |
Les articles les plus chers appartiennent à la catégorie 2.
Le CA ainsi que le nb de ventes est disparate.
# top 10 des produits ayant réalisé le plus gros CA
sel_ca = d.groupby('id_prod')['price'].sum().sort_values(ascending=False).iloc[:10]
sel_ca = pd.DataFrame({'id_prod' : sel_ca.index,
'CA réalisé' : sel_ca.values})
# quel est leur prix
sel_prix = d.loc[d['id_prod'].isin(sel_ca['id_prod']), ['price', 'id_prod']]
prix = sel_prix.drop_duplicates()
top_ca = pd.merge(sel_ca, prix, on='id_prod', how='left')
# quel est leur nb_ventes
top_ca['nb_ventes'] = top_ca['CA réalisé'] / top_ca['price']
top_ca = top_ca[['id_prod', 'nb_ventes', 'price', 'CA réalisé']]
print('Top 10 des produits ayant réalisé le plus fort CA :')
top_ca
Top 10 des produits ayant réalisé le plus fort CA :
| id_prod | nb_ventes | price | CA réalisé | |
|---|---|---|---|---|
| 0 | 2_159 | 650.0 | 145.99 | 94893.50 |
| 1 | 2_135 | 1005.0 | 68.99 | 69334.95 |
| 2 | 2_112 | 968.0 | 67.57 | 65407.76 |
| 3 | 2_102 | 1027.0 | 59.14 | 60736.78 |
| 4 | 2_209 | 814.0 | 69.99 | 56971.86 |
| 5 | 1_395 | 1875.0 | 28.99 | 54356.25 |
| 6 | 1_369 | 2252.0 | 23.99 | 54025.48 |
| 7 | 2_110 | 865.0 | 62.25 | 53846.25 |
| 8 | 2_39 | 915.0 | 57.99 | 53060.85 |
| 9 | 2_166 | 228.0 | 230.04 | 52449.12 |
Les articles ayant réalisé le plus fort CA appartiennent principalement à la catégorie 2.
Les prix sont disparates, mais le nombre de ventes est plutôt élevé.
# je recherche les valeurs communes :
# je compare les id_prod de chaque top aux autres
vc_prix_ca = top_ca.loc[top_ca['id_prod'].isin(top_prix['id_prod']), [ 'id_prod']]
print('Article(s) présent(s) dans le top ten des prix et le top ten du CA : ', vc_prix_ca.values)
vc_prix_nb = top_nb_ventes.loc[top_nb_ventes['id_prod'].isin(top_prix['id_prod']), [ 'id_prod']]
print('Article(s) présent(s) dans le top ten des prix et le top ten du nb_ventes : ', vc_prix_nb.values)
vc_nb_ca = top_ca.loc[top_ca['id_prod'].isin(top_nb_ventes['id_prod']), [ 'id_prod']]
print('Article(s) présent(s) dans le top ten du CA et le top ten du nb_ventes : ', vc_nb_ca.values)
Article(s) présent(s) dans le top ten des prix et le top ten du CA : [['2_166']] Article(s) présent(s) dans le top ten des prix et le top ten du nb_ventes : [] Article(s) présent(s) dans le top ten du CA et le top ten du nb_ventes : [['1_369']]
comp = top_ca.loc[(top_ca['id_prod'] == "2_166") | (top_ca['id_prod'] == "1_369")]
print('Ces deux articles réalisent un CA similaire avec un prix unitaire et un nombre de ventes très différent : ')
comp
Ces deux articles réalisent un CA similaire avec un prix unitaire et un nombre de ventes très différent :
| id_prod | nb_ventes | price | CA réalisé | |
|---|---|---|---|---|
| 6 | 1_369 | 2252.0 | 23.99 | 54025.48 |
| 9 | 2_166 | 228.0 | 230.04 | 52449.12 |
Nous constatons que les produits les plus chers ne sont pas ceux qui réalisent le plus de CA.
De même, les produits réalisant le CA le plus élevé ne sont pas les plus chers.
# top 10 des produits les moins vendus (nb_ventes)
sel_nb_v_f = d.groupby('id_prod')['id_prod'].count().sort_values(ascending=True).iloc[:10]
nb_v_f = pd.DataFrame({'id_prod' : sel_nb_v_f.index,
'nb_ventes' : sel_nb_v_f.values})
# quel est leur prix
nb_prix_f = d.loc[d['id_prod'].isin(sel_nb_v_f.index), ['price', 'id_prod']]
nb_prix_f = nb_prix_f.drop_duplicates()
top_nb_ventes_f = pd.merge(nb_v_f, nb_prix_f, on='id_prod', how='left')
# quel est leur CA
top_nb_ventes_f['CA réalisé'] = top_nb_ventes_f['nb_ventes'] * top_nb_ventes_f['price']
print('Top 10 des produits les moins vendus :')
top_nb_ventes_f
Top 10 des produits les moins vendus :
| id_prod | nb_ventes | price | CA réalisé | |
|---|---|---|---|---|
| 0 | 0_2201 | 1 | 20.99 | 20.99 |
| 1 | 0_1601 | 1 | 1.99 | 1.99 |
| 2 | 0_549 | 1 | 2.99 | 2.99 |
| 3 | 2_81 | 1 | 86.99 | 86.99 |
| 4 | 0_807 | 1 | 1.99 | 1.99 |
| 5 | 0_1683 | 1 | 2.99 | 2.99 |
| 6 | 0_1151 | 1 | 2.99 | 2.99 |
| 7 | 0_1633 | 1 | 24.99 | 24.99 |
| 8 | 0_833 | 1 | 2.99 | 2.99 |
| 9 | 0_886 | 1 | 21.82 | 21.82 |
Nous constatons que les articles les moins vendus appartiennent à la catégorie 0.
Leur prix se situe entre 1.99€ et 24.99€ (avec une exception pour l'article 2_81, qui appartient à la catégorie 2).
# top 10 des produits les moins chers
top_prix_f = d[['price','id_prod']].drop_duplicates().sort_values(by = 'price', ascending=True).iloc[:10]
# combien de ventes
sel_nb_v2_f = d.loc[d['id_prod'].isin(top_prix_f['id_prod']), 'id_prod']
sel_nb_v2_f = pd.DataFrame({'id_prod' : sel_nb_v2_f.values})
sel_nb_v2_f= sel_nb_v2_f.groupby('id_prod')['id_prod'].count()
nb_v2_f = pd.DataFrame({'id_prod' : sel_nb_v2_f.index,
'nb_ventes' : sel_nb_v2_f.values})
top_prix_f = pd.merge(top_prix_f, nb_v2_f, on='id_prod', how='left')
# quel est leur CA
top_prix_f['CA réalisé'] = top_prix_f['nb_ventes'] * top_prix_f['price']
top_prix_f = top_prix_f[['id_prod', 'nb_ventes', 'price', 'CA réalisé']]
print('Top 10 des produits les moins chers :')
top_prix_f
Top 10 des produits les moins chers :
| id_prod | nb_ventes | price | CA réalisé | |
|---|---|---|---|---|
| 0 | 0_202 | 9 | 0.62 | 5.58 |
| 1 | 0_528 | 9 | 0.62 | 5.58 |
| 2 | 0_120 | 15 | 0.66 | 9.90 |
| 3 | 0_1844 | 5 | 0.77 | 3.85 |
| 4 | 0_1364 | 19 | 0.81 | 15.39 |
| 5 | 0_612 | 7 | 0.88 | 6.16 |
| 6 | 0_902 | 4 | 0.92 | 3.68 |
| 7 | 0_2138 | 10 | 0.93 | 9.30 |
| 8 | 0_180 | 11 | 0.97 | 10.67 |
| 9 | 0_2166 | 18 | 0.97 | 17.46 |
Nous constatons que les produits les moins chers appartiennent à la catégorie 0.
Nous constatons également que leur nombre de ventes est faible (entre 4 et 19)
et qu'il en est de même pour le CA réalisé : inférieur à 18€.
# top 10 des produits ayant réalisé le plus petit CA
sel_ca_f = d.groupby('id_prod')['price'].sum().sort_values(ascending=True).iloc[:10]
sel_ca_f = pd.DataFrame({'id_prod' : sel_ca_f.index,
'CA réalisé' : sel_ca_f.values})
# quel est leur prix
sel_prix_f = d.loc[d['id_prod'].isin(sel_ca_f['id_prod']), ['price', 'id_prod']]
prix_f = sel_prix_f.drop_duplicates()
top_ca_f = pd.merge(sel_ca_f, prix_f, on='id_prod', how='left')
# quel est leur nb_ventes
top_ca_f['nb_ventes'] = top_ca_f['CA réalisé'] / top_ca_f['price']
top_ca_f = top_ca_f[['id_prod', 'nb_ventes', 'price', 'CA réalisé']]
print('Top 10 des produits ayant réalisé le plus petit CA :')
top_ca_f
Top 10 des produits ayant réalisé le plus petit CA :
| id_prod | nb_ventes | price | CA réalisé | |
|---|---|---|---|---|
| 0 | 0_1539 | 1.0 | 0.99 | 0.99 |
| 1 | 0_1284 | 1.0 | 1.38 | 1.38 |
| 2 | 0_1653 | 2.0 | 0.99 | 1.98 |
| 3 | 0_1601 | 1.0 | 1.99 | 1.99 |
| 4 | 0_807 | 1.0 | 1.99 | 1.99 |
| 5 | 0_541 | 1.0 | 1.99 | 1.99 |
| 6 | 0_1728 | 1.0 | 2.27 | 2.27 |
| 7 | 0_1498 | 1.0 | 2.48 | 2.48 |
| 8 | 0_898 | 2.0 | 1.27 | 2.54 |
| 9 | 0_1840 | 2.0 | 1.28 | 2.56 |
Nous constatons que les articles ayant réalisé le plus petit CA appartiennent à la catégorie 0.
Leur prix et leur nombre de vente est très bas.
# je recherche les valeurs communes :
# je compare les id_prod de chaque top aux autres
vc_prix_ca_f = top_ca_f.loc[top_ca_f['id_prod'].isin(top_prix_f['id_prod']), [ 'id_prod']]
print('Article(s) présent(s) dans le top ten des prix et le top ten du CA : ', vc_prix_ca_f.values)
vc_prix_nb_f = top_nb_ventes_f.loc[top_nb_ventes_f['id_prod'].isin(top_prix_f['id_prod']), [ 'id_prod']]
print('Article(s) présent(s) dans le top ten des prix et le top ten du nb_ventes : ', vc_prix_nb_f.values)
vc_nb_ca_f = top_ca_f.loc[top_ca_f['id_prod'].isin(top_nb_ventes_f['id_prod']), [ 'id_prod']]
print('Article(s) présent(s) dans le top ten du CA et le top ten du nb_ventes : ', vc_nb_ca_f.values)
Article(s) présent(s) dans le top ten des prix et le top ten du CA : [] Article(s) présent(s) dans le top ten des prix et le top ten du nb_ventes : [] Article(s) présent(s) dans le top ten du CA et le top ten du nb_ventes : [['0_1601'] ['0_807']]
comp_f = top_ca_f.loc[(top_ca_f['id_prod'] == "0_1601") | (top_ca_f['id_prod'] == "0_807")]
print('Ces deux articles sont parfaitement similaires : ')
comp_f
Ces deux articles sont parfaitement similaires :
| id_prod | nb_ventes | price | CA réalisé | |
|---|---|---|---|---|
| 3 | 0_1601 | 1.0 | 1.99 | 1.99 |
| 4 | 0_807 | 1.0 | 1.99 | 1.99 |
# Calcul du CA par catégorie
# group by CATEG et aggrégation (somme) sur le prix
CA_categ = d.groupby('categ')['price'].sum()
# représentation graphique
print('La répartition du CA par catégorie : ')
fig = go.Figure(data=[go.Pie(labels=['Cat 0', 'Cat 1', 'Cat 2'],
values=[CA_categ[0],CA_categ[1],CA_categ[2]])])
colors =['lightblue', 'honeydew', 'lightsteelblue']
fig.update_traces(hoverinfo='label+value',
textfont_size=15,
textinfo='label+percent',
sort=False,
#pull=[0.1, 0.1, 0.1], # on décale les quartiers
marker=dict(
colors=colors,
line=dict(color='#FFFFFF', width=2)
))
La répartition du CA par catégorie :
Pour rappel, les prix les plus élevés appartiennent à la catégorie 2 et les plus bas, à la catégorie 0.
Cette répartition semble indiquer que plus de la moitié du CA est réalisé par des articles dont le prix est plus bas.
# Répartition du CA en fonction du Prix
# group by PRICE et aggrégation sur ID_PROD (count)
CA_price = d.groupby('price')['price'].sum()
# représentation graphique
fig = go.Figure(data=go.Scattergl(
x= CA_price.index,
y = CA_price.values,
mode='markers',
marker=dict(
color=np.random.randn(3000),
colorscale='Viridis',
line_width=1)
))
fig
Ce graphique corrobore notre précédente conclusion : le CA est concentré sur des articles entre 0 et 50€.
sup = d.loc[d['price'].isin(val_aty['price']),:]
standard = d.loc[~d['price'].isin(val_aty['price']),:]
standard
| id_prod | date | session_id | client_id | sex | birth | price | categ | Année - Mois | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0_1518 | 2022-05-20 | s_211425 | c_103 | f | 1986 | 4.18 | 0 | 2022-05 |
| 1 | 0_1518 | 2021-09-26 | s_95811 | c_6197 | m | 1985 | 4.18 | 0 | 2021-09 |
| 2 | 0_1518 | 2021-05-06 | s_30782 | c_682 | f | 1974 | 4.18 | 0 | 2021-05 |
| 3 | 0_1518 | 2022-03-16 | s_180057 | c_5932 | f | 1962 | 4.18 | 0 | 2022-03 |
| 4 | 0_1518 | 2022-11-12 | s_296584 | c_7217 | f | 1976 | 4.18 | 0 | 2022-11 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 679106 | 0_1279 | 2021-06-23 | s_52563 | c_1025 | m | 1984 | 20.99 | 0 | 2021-06 |
| 679107 | 0_1683 | 2021-12-24 | s_139091 | c_5508 | f | 1974 | 2.99 | 0 | 2021-12 |
| 679108 | 0_1379 | 2021-10-16 | s_105547 | c_5425 | f | 1981 | 2.99 | 0 | 2021-10 |
| 679109 | 0_886 | 2021-03-17 | s_7410 | c_7219 | f | 1966 | 21.82 | 0 | 2021-03 |
| 679110 | 0_833 | 2021-09-27 | s_96558 | c_8260 | m | 1991 | 2.99 | 0 | 2021-09 |
673744 rows × 9 columns
##### Calcul du CA par catégorie
# group by CATEG et aggrégation (somme) sur le prix
CA_categ_sup = sup.groupby('categ')['price'].sum()
CA_categ_standard = standard.groupby('categ')['price'].sum()
# j'ajoute deux lignes à vide à la catégorie supérieure (pas d'article vendu pour les categ 0 et 1)
ligne = pd.Series([0,0])
#on concatène les 2 séries
CA_categ_sup = pd.concat([ligne, CA_categ_sup])
CA_categ_sup2 = pd.DataFrame(
columns=['categ', 'CA'],
index=['0', '1', '2' ])
CA_categ_sup2['categ'] = CA_categ_sup.index
CA_categ_sup2['CA']= CA_categ_sup.values
CA_categ_gam = pd.DataFrame(
columns=['categ', 'standard', 'sup'],
index=['0', '1', '2' ])
CA_categ_gam['categ'] = ['0', '1', '2' ]
CA_categ_gam['standard'] = CA_categ_standard.values
CA_categ_gam['sup'] = CA_categ_sup2['CA']
CA_categ_gam
| categ | standard | sup | |
|---|---|---|---|
| 0 | 0 | 4419730.97 | 0.00 |
| 1 | 1 | 4653722.69 | 0.00 |
| 2 | 2 | 1928102.09 | 852172.93 |
# mise en forme du tableau pour affichage graphique
CA_categ_gam2 = pd.DataFrame(
columns=['categ', 'CA', 'gamme'])
CA_categ_gam2['categ'] = ['0', '1', '2','0', '1', '2' ]
val2 = pd.DataFrame(CA_categ_sup2['CA'].values)
val1 = pd.DataFrame(CA_categ_standard.values)
CA_categ_gam3 = pd.concat([val1, val2])
CA_categ_gam2['CA'] = CA_categ_gam3.values
CA_categ_gam2['gamme'] = ['Standard', 'Standard', 'Standard','Supérieure', 'Supérieure', 'Supérieure' ]
px.histogram(CA_categ_gam2, x='categ', y='CA', color='gamme', barmode='group')
print('La répartition du CA par gamme : ')
px.pie(CA_categ_gam2, values='CA', names='gamme')
La répartition du CA par gamme :
Les articles de la gamme supérieure ne représentent qu'une faible proportion du CA : 7.19%
# répartition H/F
rep_sex = client.groupby('sex')['sex'].count()
print('Répartition des clients par genre : ')
px.pie(rep_sex, values='sex', names= rep_sex.index)
Répartition des clients par genre :
Une répartition plutôt égalitaire entre H et F
# répartition age
rep_age = pd.DataFrame({'birth' : client['birth'],
'age' : ''})
rep_age['age']= 2023 - rep_age['birth']
rep_age = rep_age.groupby('age')['age'].count()
#rep graphique
print('La répartition des clients par âge : ')
px.scatter(rep_age, y= 'age', x=rep_age.index)
La répartition des clients par âge :
Nous observons une concentration du nombre de clients entre 20 et 60 ans
# panier moyen
panier = d.groupby(['date', 'client_id'])['price'].sum()
panier_moy = panier.sum()/panier.count()
print('Le panier moyen est de : ', round(panier_moy, 2), '€')
Le panier moyen est de : 38.17 €
La courbe de Lorenz va nous permettre d'évaluer l'égalité de la répartition du CA entre les clients :
# Courbe de lorenz
# je sélectionne tout d'abord le sous-échantillon de travail que l'on appelle CA_lorenz
CA_lorenz = d['price']
# sel les valeurs only et les passe en négatif
CA_l = CA_lorenz.values
n = len(CA_l)
# trier les individus dans l'ordre croissant des valeurs de la variable
# Ensuite, nous calculons la somme cumulée grâce à np.cumsum()
# Pour normaliser et faire en sorte que le haut de la courbe soit à 1, on divise le tout par CA_l.sum()
lorenz = np.cumsum(np.sort(CA_l)) / CA_l.sum()
# La courbe de Lorenz commence à 0
lorenz = np.append([0],lorenz)
# La variable lorenz contient les ordonnées des points,
# mais il nous faut maintenant leurs abscisses : celles-ci s'étendent de 0 à 1
# C'est ce que produit np.linspace(0,1,len(lorenz))
#Il y a un segment de taille n pour chaque individu,
# plus 1 segment supplémentaire d'ordonnée 0.
# Le premier segment commence à 0-1/n, et le dernier termine à 1+1/n.
xaxis = np.linspace(0-1/n,1+1/n,n+1)
plt.plot(xaxis,lorenz,drawstyle='steps-post')
plt.plot([0,1], [0,1])
plt.title('Répartition du CA entre les clients :')
plt.show()
En abcisses, les effectifs cumulés
En ordonnées, le CA cumulé.
La courbe s'éloignant de la diagonale d'équirépartition (en orange), nous en déduisons que la répartition n'est pas égale.
# Gini :
AUC = (lorenz.sum() -lorenz[-1]/2 -lorenz[0]/2)/n # Surface sous la courbe de Lorenz.
#Le premier segment (lorenz[0]) est à moitié en dessous de 0, on le coupe donc en 2,
#on fait de même pour le dernier segment lorenz[-1] qui est à moitié au dessus de 1.
S = 0.5 - AUC # surface entre la première bissectrice et le courbe de Lorenz
gini = 2*S
gini
0.39547021795694925
L'indice de Gini mesure ici l'inégalité des chiffres d'affaires de tous les clients.
NB : Indice de Gini : chiffre de 0 à 1 mesurant l'aire entre la bissectrice et la courbe de Lorenz
O indique une égalité parfaite (répartition parfaitement égale)
1 indique une inégalité parfaite (un seul client réalise le CA total)
Notre indice de 0.39 confirme l'inégalité de répartition du CA entre les clients. Cette inégalité reste modérée.
Pour aller plus loin dans l'analyse de ce dernier point, nous allons présenter la répartition des clients par CA :
# répartition du CA par client
rep_ca_client = d.groupby('client_id')['price'].sum().sort_values(ascending=False)
rep_ca_client.head(15)
# rep_ca_client.describe()
client_id c_1609 324033.35 c_4958 289760.34 c_6714 153598.92 c_3454 113637.93 c_3263 5276.87 c_1570 5271.62 c_2899 5214.05 c_2140 5208.82 c_7319 5155.77 c_7959 5085.45 c_8026 5082.58 c_7421 5050.20 c_5263 5006.85 c_1422 4993.55 c_1876 4970.62 Name: price, dtype: float64
ca_moy = CA_total/8600
Nous constatons que 4 clients réalisent un CA très supérieur aux autres clients entre 110 000 et 350 000 € chacun. Pour rappel, le CA moyen par client est de : 1 378€. Il pourrait s'agir de clients professionnels.
# répartition du CA entre clients B2B et B2C
rep_ca_client_pro = pd.DataFrame({'CA' : rep_ca_client.values,
'type' : 'B2C'})
rep_ca_client_pro.loc[0:3, 'type'] = 'B2B'
rep_ca_client_pro['CA'].astype(float)
# graph répartition
rep_ca_c_pro = rep_ca_client_pro.groupby('type')['CA'].sum()
print('Répartition du CA par type de client : ')
px.pie(rep_ca_c_pro, values=rep_ca_c_pro.values, names= rep_ca_c_pro.index)
Répartition du CA par type de client :
Les clients de type B2C, professionnels, représentent 7.43% du CA.
# répartition du CA par genre
rep_ca_sex = d.groupby('sex')['price'].sum()
print('Répartition du CA par genre : ')
px.pie(rep_ca_sex, values=rep_ca_sex.values, names= rep_ca_sex.index)
Répartition du CA par genre :
Nous constatons à nouveau une répartition homogène entre les hommes et les femmes.
# répartition du CA par âge
# répartition age
rep_ca_age = d.groupby('birth')['price'].sum()
rep_ca_age_date = pd.DataFrame({'Birth' : rep_ca_age.index,
'CA' : rep_ca_age.values})
rep_ca_age_date['Age'] = 2023 - rep_ca_age_date['Birth']
# #rep graphique
print('La répartition des clients par âge : ')
px.scatter(rep_ca_age_date, x= 'Age', y='CA')
La répartition des clients par âge :
Nous constatons que le CA semble être fonction décroissant de l'âge.
Au cours de l'analyse du CA réalisée précédemment, nous avons fait les observations suivantes :
Nous allons vérifier nos hypothèses en testant les corrélations identifiées.
# Nombre total d'articles vendus par client
d_an = d.merge(
d.groupby('client_id').count()['date'].reset_index().rename(columns={'date': 'NbT Articles'}),
how='left', on='client_id')
# nombre total de ventes par client
d_an = d_an.merge(
d_an.groupby("client_id")["session_id"].nunique().reset_index().rename(columns= {'session_id': 'Nb Ventes'}),
how='left', on='client_id')
# age
d_an['age'] = 2023 - d_an['birth']
# CA total par client
d_an = d_an.merge(
d_an.groupby("client_id")["price"].sum().reset_index().rename(columns= {'price': 'CA Total'}),
how='left', on='client_id')
# Panier moyen par client
d_an['Panier Moy CA'] = d_an['CA Total']/d_an['Nb Ventes']
# Nb moyen d'article par panier par client
d_an['Nb Moy Art/panier'] = d_an['NbT Articles']/d_an['Nb Ventes']
# fréquence d'achat par client
d_an['Fréquence achat'] = round(d_an['Nb Ventes']/24,2)
d_an[d_an['client_id'] == 'c_100']
| id_prod | date | session_id | client_id | sex | birth | price | categ | Année - Mois | NbT Articles | Nb Ventes | age | CA Total | Panier Moy CA | Nb Moy Art/panier | Fréquence achat | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 108537 | 1_442 | 2021-08-10 | s_73929 | c_100 | m | 1992 | 24.98 | 1 | 2021-08 | 8 | 5 | 31 | 254.85 | 50.97 | 1.6 | 0.21 |
| 194408 | 1_57 | 2021-06-24 | s_53051 | c_100 | m | 1992 | 11.99 | 1 | 2021-06 | 8 | 5 | 31 | 254.85 | 50.97 | 1.6 | 0.21 |
| 220935 | 1_547 | 2022-07-20 | s_240961 | c_100 | m | 1992 | 15.99 | 1 | 2022-07 | 8 | 5 | 31 | 254.85 | 50.97 | 1.6 | 0.21 |
| 220955 | 1_547 | 2021-04-20 | s_23060 | c_100 | m | 1992 | 15.99 | 1 | 2021-04 | 8 | 5 | 31 | 254.85 | 50.97 | 1.6 | 0.21 |
| 221075 | 1_547 | 2022-09-20 | s_270695 | c_100 | m | 1992 | 15.99 | 1 | 2022-09 | 8 | 5 | 31 | 254.85 | 50.97 | 1.6 | 0.21 |
| 317394 | 2_194 | 2021-06-24 | s_53051 | c_100 | m | 1992 | 138.53 | 2 | 2021-06 | 8 | 5 | 31 | 254.85 | 50.97 | 1.6 | 0.21 |
| 429118 | 0_1111 | 2021-08-10 | s_73929 | c_100 | m | 1992 | 19.99 | 0 | 2021-08 | 8 | 5 | 31 | 254.85 | 50.97 | 1.6 | 0.21 |
| 661274 | 0_312 | 2021-08-10 | s_73929 | c_100 | m | 1992 | 11.39 | 0 | 2021-08 | 8 | 5 | 31 | 254.85 | 50.97 | 1.6 | 0.21 |
# je réorganise le tableau
d_an.drop(columns=["date", "birth", "Année - Mois"], inplace=True)
d_an = d_an[['client_id', 'sex', 'age', 'session_id', 'id_prod', 'price', 'categ', 'CA Total', 'NbT Articles', 'Nb Ventes',
'Panier Moy CA', 'Nb Moy Art/panier', 'Fréquence achat' ]]
d_an[d_an['client_id'] == 'c_100']
| client_id | sex | age | session_id | id_prod | price | categ | CA Total | NbT Articles | Nb Ventes | Panier Moy CA | Nb Moy Art/panier | Fréquence achat | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 108537 | c_100 | m | 31 | s_73929 | 1_442 | 24.98 | 1 | 254.85 | 8 | 5 | 50.97 | 1.6 | 0.21 |
| 194408 | c_100 | m | 31 | s_53051 | 1_57 | 11.99 | 1 | 254.85 | 8 | 5 | 50.97 | 1.6 | 0.21 |
| 220935 | c_100 | m | 31 | s_240961 | 1_547 | 15.99 | 1 | 254.85 | 8 | 5 | 50.97 | 1.6 | 0.21 |
| 220955 | c_100 | m | 31 | s_23060 | 1_547 | 15.99 | 1 | 254.85 | 8 | 5 | 50.97 | 1.6 | 0.21 |
| 221075 | c_100 | m | 31 | s_270695 | 1_547 | 15.99 | 1 | 254.85 | 8 | 5 | 50.97 | 1.6 | 0.21 |
| 317394 | c_100 | m | 31 | s_53051 | 2_194 | 138.53 | 2 | 254.85 | 8 | 5 | 50.97 | 1.6 | 0.21 |
| 429118 | c_100 | m | 31 | s_73929 | 0_1111 | 19.99 | 0 | 254.85 | 8 | 5 | 50.97 | 1.6 | 0.21 |
| 661274 | c_100 | m | 31 | s_73929 | 0_312 | 11.39 | 0 | 254.85 | 8 | 5 | 50.97 | 1.6 | 0.21 |
# Heatmaps et matrice de corrélation
# poser les hypothèses
# Choix du test selon
# - homogénéité
# - normalité
# - type de variable
# déroulement du test
# évt post-hoc
# décision
# conclusion
# matrice clients :
d_an_c = d_an.set_index('client_id')
d_an_c.drop(columns=["session_id", "id_prod", "price", 'categ'], inplace=True)
d_an_c = d_an_c.drop_duplicates()
d_an_c
| sex | age | CA Total | NbT Articles | Nb Ventes | Panier Moy CA | Nb Moy Art/panier | Fréquence achat | |
|---|---|---|---|---|---|---|---|---|
| client_id | ||||||||
| c_103 | f | 37 | 2288.49 | 195 | 89 | 25.713371 | 2.191011 | 3.71 |
| c_6197 | m | 38 | 1519.18 | 117 | 45 | 33.759556 | 2.600000 | 1.88 |
| c_682 | f | 49 | 4102.18 | 331 | 161 | 25.479379 | 2.055901 | 6.71 |
| c_5932 | f | 61 | 2709.81 | 161 | 111 | 24.412703 | 1.450450 | 4.62 |
| c_7217 | f | 47 | 2418.01 | 181 | 74 | 32.675811 | 2.445946 | 3.08 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| c_4478 | f | 53 | 13.36 | 1 | 1 | 13.360000 | 1.000000 | 0.04 |
| c_4540 | m | 20 | 363.98 | 2 | 2 | 181.990000 | 1.000000 | 0.08 |
| c_4391 | f | 22 | 363.98 | 2 | 2 | 181.990000 | 1.000000 | 0.08 |
| c_1232 | f | 63 | 39.70 | 2 | 2 | 19.850000 | 1.000000 | 0.08 |
| c_7534 | m | 31 | 201.98 | 2 | 2 | 100.990000 | 1.000000 | 0.08 |
8600 rows × 8 columns
sns.pairplot(d_an_c.iloc[:,0:9])
<seaborn.axisgrid.PairGrid at 0x276c6e161f0>
sns.set(rc={'figure.figsize':(10,4)})
d_an_c_corr = d_an_c.corr()
ax = sns.heatmap(d_an_c_corr, xticklabels = d_an_c_corr.columns ,
yticklabels = d_an_c_corr.columns, cmap = 'coolwarm')
1/ matrice des corrélations ou heatmap
2/ corrélations à faire :
genre / catégorie
age / CA
taille panier / panier moyen
age / fq achat
fq achat / taille panier
age / panier moyen
fq achat / panier moyen
taille panier / cat
cat / age
cat / fq achat
cat / panier moyen
age / taille panier
CA et genre
CA et prix
Nb Ventes et prix
Nb Ventes et catégorie
NbV et CA